package cc.sends;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;

public class RR {
    private int mProcessCount;
    private Queue<Process> mReadyQueue;
    private int mTimeSlice; //时间片
    private Queue<Process> mUnreachQueue; //未结束的进程队列
    private Queue<Process> mExecutedQueue; //已结束的进程队列
    private double mTotalWholeTime = 0.0;
    private double mTotalWeightWholeTime = 0.0;

    private RR(int mProcessCount, Queue<Process> mUnreachQueue, int mTimeSlice) {
        this.mProcessCount = mProcessCount;
        this.mUnreachQueue = mUnreachQueue;
        this.mReadyQueue = new LinkedBlockingQueue<>();
        this.mTimeSlice = mTimeSlice;
        this.mExecutedQueue = new LinkedList<>();
    }

    private void RRAlgorithm() {
        mReadyQueue.add(mUnreachQueue.poll());
        Process currProcess = mReadyQueue.poll();
        int currTime = executeProcess(currProcess, 0);
        while(!mReadyQueue.isEmpty() || !mUnreachQueue.isEmpty()) {
            while(!mUnreachQueue.isEmpty()) {
                if(mUnreachQueue.peek().getArrivalTime() <= currTime) {
                    mReadyQueue.add(mUnreachQueue.poll());
                } else {
                    break;
                }
            }
            if(currProcess.getRemainServiceTime() > 0) mReadyQueue.add(currProcess);
            if(!mReadyQueue.isEmpty()) {
                currProcess = mReadyQueue.poll();
                currTime = executeProcess(currProcess, currTime);
            }
            else {
                currTime = mUnreachQueue.peek().getArrivalTime();
            }
        }
    }

    private int executeProcess(Process currProcess, int currTime){
        if(currProcess.getRemainServiceTime() - mTimeSlice <= 0) {
            showExecuteMessage(currTime, currTime += currProcess.getRemainServiceTime(), currProcess.getName());
            currProcess.setFinishTime(currTime);
            currProcess.setRemainServiceTime(0);
            calculateWholeTime(currProcess);
            calculateWeightWholeTime(currProcess);
            mTotalWholeTime += currProcess.getWholeTime();
            mTotalWeightWholeTime += currProcess.getWeightWholeTime();
            mExecutedQueue.add(currProcess);
        }
        else {
            showExecuteMessage(currTime, currTime += mTimeSlice, currProcess.getName());
            currProcess.setRemainServiceTime(currProcess.getRemainServiceTime() - mTimeSlice);
        }
        return currTime;
    }

    private void calculateWholeTime(Process process){
        process.setWholeTime(process.getFinishTime() - process.getArrivalTime());
    }

    private void calculateWeightWholeTime(Process process) {
        process.setWeightWholeTime(process.getWholeTime() / (double)process.getServiceTime());
    }

    private void showExecuteMessage(int startTime, int endTime, String name) {
        System.out.println(startTime + "--" + endTime + ":[Process" + name + "] is running");
    }

    private void showResult() {
        System.out.print("Process | ");
        System.out.print("turnaround time | ");
        System.out.println("weight turnaround time ");
        Process process;
        while (!mExecutedQueue.isEmpty()) {
            process = mExecutedQueue.poll();
            System.out.print("Process"+process.getName() + " ");
            System.out.print("        " + process.getWholeTime() + " ");
            System.out.println("             " + process.getWeightWholeTime() + " ");
        }
        System.out.println("average turnaround time:" + mTotalWholeTime / (double) mProcessCount);
        System.out.println("average weight turnaround time:" + mTotalWeightWholeTime / (double) mProcessCount);
    }

//    private static void printAll(Queue<Process> queue) {
//        System.out.print("Process ");
//        System.out.print("reach time ");
//        System.out.println("running time ");
//        Process process;
//        while (!queue.isEmpty()){
//            process = queue.poll();
//            System.out.print("Process"+process.getName() + "");
//            System.out.print(" " + process.getArrivalTime() + " ");
//            System.out.println(" " + process.getServiceTime() + " ");
//        }
//    }

    public static void RRProcess(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("Input the count for processes:");
        int processCount = scanner.nextInt();
        if (processCount < 1)
            return;
        Queue<Process> queue = new LinkedBlockingQueue();
        System.out.println("Input the reach time and service time for every process:");
        for (int i = 0; i < processCount; i++) {
            Process process = new Process((char)(i+65) + "");
            process.setArrivalTime(scanner.nextInt());
            int time = scanner.nextInt();
            process.setServiceTime(time);
            process.setRemainServiceTime(time);
            queue.add(process);
        }
        System.out.println("Input the time slice:");
        int timeSlice = scanner.nextInt();
        RR rr = new RR(processCount, queue, timeSlice);
        rr.RRAlgorithm();
        rr.showResult();
    }


}
